package com.polygmirror.simple.service;

import com.polygmirror.simple.model.Question;
import com.polygmirror.simple.model.ResumeSetting;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Description
 * 面试题连环炮模式抽取算法实践
 * date: 2022/8/3
 *
 * @author shenshuai
 * @version 1.0.0
 * @since JDK 1.8
 */
public class ChainQuestionMatchService extends AbstractQuestionMatchService {
    @Override
    public List<Question> questionMatch(ResumeSetting resumeSetting, List<Question> allQuestionList) {
        List<Question> preMatchQuestionList = matches(resumeSetting.getTags(), allQuestionList);
        Map<String, List<Question>> categoryQuestionMap = new HashMap<>();
        for (Question question : preMatchQuestionList) {
            List<Question> questionList = categoryQuestionMap.getOrDefault(question.getCategoryKey(), new ArrayList<>());
            questionList.add(question);
            categoryQuestionMap.put(question.getCategoryKey(), questionList);
        }
        List<List<Question>> categoryQuestionList = new ArrayList<>();

        for (Map.Entry<String, List<Question>> entry : categoryQuestionMap.entrySet()) {
            categoryQuestionList.add(entry.getValue());
        }

        int count = resumeSetting.getExpectCount();

        List<Question> resultQuestionList = new ArrayList<>();
        Set<String> resultQuestionSet = new HashSet<>();
        Stack<Integer> stack = getRandomKeyStack(categoryQuestionList.size());
        while (count > 0){
            Map<String, List<Question>> questionTypeGroupMap;
            if(stack.isEmpty()){
                stack = getRandomKeyStack(categoryQuestionList.size());
            }
            questionTypeGroupMap = categoryQuestionList.get(stack.pop()).stream().collect(Collectors.groupingBy(Question::getQuestionType));
            for (Map.Entry<String, List<Question>> entryQuestion : questionTypeGroupMap.entrySet()) {
                Map<Integer, List<Question>> questionIndexGroupMap = entryQuestion.getValue().stream().collect(Collectors.groupingBy(Question::getQuestionIndex));

                int randomCount = 1;
                if (questionIndexGroupMap.size() > 5) {
                    randomCount = 3;
                }
                if(questionIndexGroupMap.size() >= 2 && questionIndexGroupMap.size() < 5 ){
                    randomCount = 2;
                }
                while (randomCount > 0) {
                    Random random = new Random();
                    int randomIndex = random.nextInt(questionIndexGroupMap.size()) + 1;
                    List<Question> questionList = questionIndexGroupMap.get(randomIndex);
                    if(questionList == null){
                        randomCount--;
                        continue;

                    }
                    questionList.sort(Comparator.comparing(Question::getOrder));
                    if (questionList.size() <= 3) {
                        count = count - questionList.size();
                        if(resultQuestionSet.contains(questionList.get(0).getQuestionContent())){
                            randomCount--;
                            continue;
                        }
                        questionList.stream().forEach(question -> {
                            resultQuestionSet.add(question.getQuestionContent());
                        });
                        resultQuestionList.addAll(questionList);
                    } else {
                        if (count <= 0) {
                            return resultQuestionList;
                        }
                        if(resultQuestionSet.contains(questionList.get(0).getQuestionContent())
                            || resultQuestionSet.contains(questionList.get(1).getQuestionContent())){
                            randomCount--;
                            continue;
                        }
                        resultQuestionList.add(questionList.get(0));
                        resultQuestionList.add(questionList.get(1));
                        resultQuestionSet.add(questionList.get(0).getQuestionContent());
                        resultQuestionSet.add(questionList.get(1).getQuestionContent());

                        List<Integer> questionIndexList = new ArrayList<>();
                        int chainCount = questionList.size() - 2 > 4 ? 3 : 2;
                        for (int j = 0; j < chainCount; j++) {
                            int randomx = (int) (3 + Math.random() * (questionList.size() - 3));
                            if (!questionIndexList.contains(randomx)) {
                                questionIndexList.add(randomx);
                            }
                        }
                        questionIndexList.stream().sorted().collect(Collectors.toList());

                        questionIndexList.stream().forEach(index -> {
                            if(index < questionList.size() && !resultQuestionSet.contains(questionList.get(index).getQuestionContent())){
                                resultQuestionList.add(questionList.get(index));
                                resultQuestionSet.add(questionList.get(index).getQuestionContent());
                            }
                        });
                        count = count - 2 - questionIndexList.size();
                    }
                    randomCount--;
                }
            }

        }

        return resultQuestionList;
    }


    private Stack<Integer> getRandomKeyStack(Integer size){
        Stack<Integer> stack = new Stack<>();
        int [] arr = new int[size];
        for (int i = 0;i < size;i++){
            arr[i] = i;
        }

        for (int i = 0;i< size;i++){
            Random random = new Random();
            int first = random.nextInt(size);
            int second = random.nextInt(size);
            int tmp = arr[first];
            arr[first] = arr[second];
            arr[second] = tmp;
        }

        for (int i = 0;i< size;i++){
            stack.push(arr[i]);
        }

        return stack;
    }



}
